/**
 * \file  possible_mutants.cpp
 * \brief implementation of possible_mutants class.
 *
 * <http://lav.googlecode.com/>
 * Copyright (C) 2013 Alberione Fernando, Ocampo Emanuel,  FuDePAN
 *
 * This file is part of the LAV project.
 *
 * Contents:      CPP file for LAV providing class possible_mutants.
 *
 * System:         LAV
 * Language:       C++
 *
 * Author:         Alberione Fernando
 * E-Mail:         falberione037@gmail.com
 *
 * Author:         Ocampo Emanuel
 * E-Mail:         eocampo86@gmail.com
 *
 * LAV is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LAV is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LAV.  If not, see <http://www.gnu.org/licenses/>.
 *
 */


#include <mili/mili.h>
#include "lav/possible_mutants.h"

using namespace std;
namespace lav
{

void PossibleMutants::AminoacidTriplets::get_triplet(MultibaseNumberDigit digit, biopp::Triplet& triplet) const
{
    assert(digit <= triplets.size());
    triplet = triplets[digit];
}

void PossibleMutants::PosAminoacidsTriplets::get_triplet_of_digit(MultibaseNumberDigit digit, biopp::Triplet& triplet) const
{

    uint triplet_position = digit;
    AminoacidTripletsContainer::const_iterator it_amin_triplets = aminoacids_triplets.begin();
    search_position_triplet(it_amin_triplets, triplet_position);
    it_amin_triplets->get_triplet(triplet_position, triplet);
}


void PossibleMutants::PosAminoacidsTriplets::get_digit_of_triplet(const biopp::Triplet& triplet, MultibaseNumberDigit& digit) const
{
    digit = 0;
    bool found = false;
    uint sum_triplets = 0;
    AminoacidTripletsContainer::const_iterator it_amin_triplets = aminoacids_triplets.begin();
    while ((it_amin_triplets != aminoacids_triplets.end()) && (!found))
    {
        found = exist_triplet(triplet, it_amin_triplets, sum_triplets);
        ++it_amin_triplets;
    }
    if (found)
    {
        digit = sum_triplets;
    }
}

PossibleMutants::Id PossibleMutants::get_maxNumber_mutants() const
{
    return maxNumberMutants;
}

size_t PossibleMutants::get_mutantSize() const
{
    return mutantSize;
}


size_t PossibleMutants::get_numberBases() const
{
    return mutantsBases.size();
}


size_t PossibleMutants::get_numberMutants_coefficients() const
{
    return mutantsCoefficients.size();
}


size_t PossibleMutants::get_base(size_t pos) const
{
    assert(pos < mutantSize);
    return mutantsBases[pos];
}


size_t PossibleMutants::get_coefficient(size_t pos) const
{
    assert(pos < mutantSize);
    return mutantsCoefficients[pos];
}

PossibleMutants::Id PossibleMutants::get_id(const MutantSeq& mutant) const
{
    assert(mutantSize == mutant.size());
    Id id = 0;
    for (size_t i = 1; i <= mutantSize; ++i)
    {
        id += mutant[i - 1] * mutantsCoefficients[mutantSize - i];
    }
    return id;
}


void PossibleMutants::get_mutant(Id id, MutantSeq& mutant) const
{
    assert(id <= maxNumberMutants);
    mutant.resize(mutantSize);
    uint64_t cociente = id;
    for (size_t i = mutantSize; i > 1; --i)
    {
        mutant[i - 1] = cociente % mutantsBases[i - 1];
        cociente /= mutantsBases[i - 1];
    }
    mutant[0] = cociente;
}


void PossibleMutants::mutant_to_triplets(const PossibleMutants::PosAminoacidsTripletsContainer& join_antivirals,
        const PossibleMutants::MutantSeq& mutant, PossibleMutants::TripletPositionContainer& triplets_positions) const
{
    biopp::Triplet current_triplet;
    for (size_t i = 0; i < mutant.size(); ++i)
    {
        if (mutant[i] > 0)
        {
            join_antivirals[i].get_triplet_of_digit(mutant[i], current_triplet);
            PossibleMutants::TripletPosition current_pos_triplet(join_antivirals[i].pos_resistance, current_triplet);
            mili::insert_into(triplets_positions, current_pos_triplet);
        }
    }
}

// -----------------------
// Private methods section
// -----------------------

void PossibleMutants::PosAminoacidsTriplets::search_position_triplet(AminoacidTripletsContainer::const_iterator& it_amin_triplets,
        uint& pos) const
{
    bool found = false;
    while ((it_amin_triplets != aminoacids_triplets.end()) && (!found))
    {
        if (it_amin_triplets->triplets.size() >= pos)
        {
            assert(pos > 0);
            --pos;
            found = true;
        }
        else
        {
            // pos > it_amin_triplets->triplets.size()
            pos -= it_amin_triplets->triplets.size();
            ++it_amin_triplets;
        }
    }
}

bool PossibleMutants::PosAminoacidsTriplets::exist_triplet(const biopp::Triplet& triplet,
        const AminoacidTripletsContainer::const_iterator& it_amin_triplets, uint& sum_triplets) const
{
    bool found = false;
    uint current_pos = 0;
    biopp::Triplet current_triplet;
    biopp::NucDistance distance = 3;
    biopp::DistanceMatrix m;
    biopp::initialize_distance_matrix(m);
    while (current_pos < it_amin_triplets->triplets.size() && (!found))
    {
        it_amin_triplets->get_triplet(current_pos, current_triplet);
        distance = triplet.distance(current_triplet, m);
        if (distance == 0)
        {
            found = true;
        }
        ++sum_triplets;
        ++current_pos;
    }
    return found;
}

void PossibleMutants::get_mutant_bases(const PosAminoacidsTripletsContainer& join_antivirals)
{
    PosAminoacidsTripletsContainer::const_iterator it_join;
    AminoacidTripletsContainer amins_triplets;
    size_t i = 0;
    for (it_join = join_antivirals.begin(); it_join != join_antivirals.end(); ++it_join)
    {
        amins_triplets = it_join->aminoacids_triplets;
        mutantsBases[i] =  get_sum_triplets(amins_triplets) + 1;
        ++i;
    }
}

uint PossibleMutants::get_sum_triplets(const AminoacidTripletsContainer& amins_triplets)
{
    uint cant_triplets = 0;
    AminoacidTripletsContainer::const_iterator it_amins_triplets;
    for (it_amins_triplets = amins_triplets.begin(); it_amins_triplets != amins_triplets.end(); ++it_amins_triplets)
    {
        cant_triplets += (it_amins_triplets->triplets).size();
    }
    return cant_triplets;
}

}